#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <string.h>
#include <list>
#include <map>
#include <sys/ioctl.h>
#include <net/if.h>
#include "definitions.hpp"

using namespace std;


unsigned short in_cksum(unsigned short *addr, int len)
{
    int nleft = len;
    int sum = 0;
    unsigned short *w = addr;
    unsigned short answer = 0;

    while (nleft > 1) {
        sum += *w++;
        nleft -= 2;
    }

    if (nleft == 1) {
        *(unsigned char *)(&answer) = *(unsigned char *)w;
        sum += answer;
    }

    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    answer = ~sum;
    return (answer);
}


u_char * nametoaddr(const char * devname)
{
         static struct ifreq ifr;
         int s;
 
         memset(&ifr, 0, sizeof(ifr));
         if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
                 fprintf(stderr, "Ethernet::nametoaddr-- failed socket\n");
                 return NULL;
         }
         strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name));
         if (ioctl(s, SIOCGIFADDR, (char *)&ifr) < 0) {
                 fprintf(stderr, "Ethernet::nametoaddr-- failed SIOCGIFADDR\n");
                 return NULL;
         }
         return ((u_char*) &ifr.ifr_data);
}


// Global Variables Begin //

list<string> interface_list;		
map<string,string> ipadd_map;

list<unsigned char*> packet_q_i1;
list<unsigned char*> packet_q_i2;

// Global Variables End //


// Parse ARP Begin //

void parse_arp(){
	char buffer[4000];
	FILE *fp = fopen("/proc/net/arp","r");
	if(fp == NULL){
		cerr<<"Error in opening file! Please try again\n";
		//exit(1);
	}
        bzero(buffer,4000);
	int loop_counter = 0;
	while(fgets(buffer,4000,fp)!=NULL){
	loop_counter++;
		if(loop_counter == 1){
			continue;
		}else{
			//Parse buffer here
			int inner_loop = 0;
			char *pch;
			pch = strtok (buffer," ");
			string ipadd;
  			while (pch != NULL)
  			{
				inner_loop++;

				if(inner_loop == 1) // Parse for IPAddress, making the last 8 Bits zero
				{	ipadd.clear();
					ipadd = string(pch);
					//ipadd[ipadd.length()-1] = '0';
				}
				
				if(inner_loop == 4)
				{
					string macadd = string(pch);
			
					
					pch = strtok (NULL," ");
					pch = strtok (NULL," ");
					
					string inter = string(pch);
					inter = inter.substr(0,inter.length()-1);
					//push to interface list
					//interface_list.push_back(inter);
					
                                        string value = macadd + "," + inter;
					//push to ip address map
					if(ipadd.compare("10.10.0.1") == 0){
					//ipadd_map[ipadd] = value;
					ipadd_map["10.10.0"] = value;
					interface_list.push_front(inter);
					}else if(ipadd.compare("10.99.0.2") == 0 ){
						ipadd_map["10.1.2"] = value;
						ipadd_map["10.1.3"] = value;
                    interface_list.push_back(inter);
					}else if(ipadd.compare("10.99.0.1") == 0 ){
						ipadd_map["10.1.0"] = value;
					}else;

					break;
				}
			       pch = strtok (NULL, " ");
 		        }	
		}
	
	bzero(buffer,4000);

	}

}

// Parse ARP End //

int main(){
 
  string pingstring[3];
  char ip[256];
	
  pingstring[0] = "10.10.0.1";
  pingstring[1] = "10.99.0.1";
  pingstring[2] = "10.99.0.2";

  // Populate ARP begin //	
  for(int i = 0; i<3; i++){
  bzero(ip,256);
  sprintf(ip,"ping -c 4 %s",pingstring[i].c_str());
  system(ip);
  }
  // Populate ARP end //

  // Populate Tables on Memory Begin//
  parse_arp();	
  // Populate Tables on Memory End//

        pthread_t icmp_thread;
            pthread_attr_t attr_icmp;
            int thrd_icmp=0;
            void* status_icmp;
            
            pthread_attr_init(&attr_icmp);
            pthread_attr_setdetachstate(&attr_icmp, PTHREAD_CREATE_JOINABLE);
            
            thrd_icmp=pthread_create(&icmp_thread,&attr_icmp,icmp_thread_func,(void *)1);
            if(thrd_icmp){
                cerr<<"Error; return code from thread icmp create call is"<<thrd_icmp<<endl;
                exit(-1);
            } 
            
  pthread_attr_destroy(&attr_icmp);


  // Create Sniffer1 Thread Begin //
  pthread_t sniffer1_thread;
            pthread_attr_t attr_sniffer1;
            int thrd_sniffer1=0;
            void* status_sniffer1;
            
            pthread_attr_init(&attr_sniffer1);
            pthread_attr_setdetachstate(&attr_sniffer1, PTHREAD_CREATE_JOINABLE);
            
            thrd_sniffer1=pthread_create(&sniffer1_thread,&attr_sniffer1,sniffer1_thread_func,(void *)1);
            if(thrd_sniffer1){
                cerr<<"Error; return code from thread sniffer1 create call is"<<thrd_sniffer1<<endl;
                exit(-1);
            } 
            
  pthread_attr_destroy(&attr_sniffer1);

  // Sniffer1 Thread End          //

  pthread_t rdwrif1_thread;
            pthread_attr_t attr_rdwrif1;
            int thrd_rdwrif1=0;
            void* status_rdwrif1;
            
            pthread_attr_init(&attr_rdwrif1);
            pthread_attr_setdetachstate(&attr_rdwrif1, PTHREAD_CREATE_JOINABLE);
            
            thrd_rdwrif1=pthread_create(&rdwrif1_thread,&attr_rdwrif1,rdwrif1_thread_func,(void *)1);
            if(thrd_rdwrif1){
                cerr<<"Error; return code from thread rdwrif1 create call is"<<thrd_rdwrif1<<endl;
                exit(-1);
            } 
            
  pthread_attr_destroy(&attr_rdwrif1);


    // Create sniffer2 Thread Begin //
  pthread_t sniffer2_thread;
            pthread_attr_t attr_sniffer2;
            int thrd_sniffer2=0;
            void* status_sniffer2;
            
            pthread_attr_init(&attr_sniffer2);
            pthread_attr_setdetachstate(&attr_sniffer2, PTHREAD_CREATE_JOINABLE);
            
            thrd_sniffer2=pthread_create(&sniffer2_thread,&attr_sniffer2,sniffer2_thread_func,(void *)1);
            if(thrd_sniffer2){
                cerr<<"Error; return code from thread sniffer2 create call is"<<thrd_sniffer2<<endl;
                exit(-1);
            } 
            
  pthread_attr_destroy(&attr_sniffer2);

  // sniffer2 Thread End          //

    pthread_t rdwrif2_thread;
            pthread_attr_t attr_rdwrif2;
            int thrd_rdwrif2=0;
            void* status_rdwrif2;
            
            pthread_attr_init(&attr_rdwrif2);
            pthread_attr_setdetachstate(&attr_rdwrif2, PTHREAD_CREATE_JOINABLE);
            
            thrd_rdwrif2=pthread_create(&rdwrif2_thread,&attr_rdwrif2,rdwrif2_thread_func,(void *)1);
            if(thrd_rdwrif2){
                cerr<<"Error; return code from thread rdwrif2 create call is"<<thrd_rdwrif2<<endl;
                exit(-1);
            } 
            
  pthread_attr_destroy(&attr_rdwrif2);





  thrd_sniffer1=pthread_join(sniffer1_thread,&status_sniffer1);
            if(thrd_sniffer1){
                cerr<<"Error; return code from thread sniffer1 join call is "<<thrd_sniffer1<<endl;
                exit(-1);
            }

 
  thrd_sniffer2=pthread_join(sniffer2_thread,&status_sniffer2);
            if(thrd_sniffer2){
                cerr<<"Error; return code from thread sniffer2 join call is "<<thrd_sniffer2<<endl;
                exit(-1);
            }


  thrd_rdwrif1=pthread_join(rdwrif1_thread,&status_rdwrif1);
            if(thrd_rdwrif1){
                cerr<<"Error; return code from thread rdwrif1 join call is "<<thrd_rdwrif1<<endl;
                exit(-1);
            } 
  
  thrd_rdwrif2=pthread_join(rdwrif2_thread,&status_rdwrif2);
            if(thrd_rdwrif2){
                cerr<<"Error; return code from thread rdwrif2 join call is "<<thrd_rdwrif2<<endl;
                exit(-1);
            }

  thrd_icmp=pthread_join(icmp_thread,&status_icmp);
            if(thrd_icmp){
                cerr<<"Error; return code from thread rdwrif2 join call is "<<thrd_icmp<<endl;
                exit(-1);
            }


  return 1;
}


